package com.edu.tool.service.impl.sms;



import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.edu.common.util.HttpClientUtils;
import com.edu.common.util.PropertiesFileUtil;
import com.edu.common.util.SimpleStringUtil;
import com.edu.common.util.alisms.AliyunMessageUtil;
import com.edu.common.util.alisms.SmsConstant;

import com.edu.common.util.xml.XmlUtil;
import com.edu.tool.bean.user.SysAuthToken;
import com.edu.tool.bean.user.SysTenantDictConf;
import com.edu.tool.bean.sms.SysSms;
import com.edu.tool.bean.sms.SysSmsTemplate;
import com.edu.tool.constant.MsgConstant;
import com.edu.tool.constant.RetObjConstant;
import com.edu.tool.exception.sms.SmsException;
import com.edu.tool.mapper.conf.SysTenantDictConfMapper;
import com.edu.tool.mapper.user.SysAuthTokenMapper;
import com.edu.tool.mapper.sms.SysSmsMapper;
import com.edu.tool.mapper.sms.SysSmsTemplateMapper;
import com.edu.tool.service.user.SysTenantService;
import com.edu.tool.service.sms.SysSmsService;
import com.edu.tool.util.MsgUtil;
import com.edu.tool.util.date.SimpleDateUtil;
import com.edu.tool.util.json.JsonUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.conn.ConnectTimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.w3c.dom.Element;

import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.*;

/**
 * @FileName：SmsServiceImpl.java
 * @Description：@TODO
 * @Author：thy
 * @CreateTime：上午10:50:55
 */
@Service
public class SysSmsServiceImpl implements SysSmsService {

    private static final Logger log = LoggerFactory.getLogger(SysSmsServiceImpl.class);

    @Autowired
    private SysSmsMapper smsMapper;

    @Autowired
    private SysTenantDictConfMapper tenantDictConfMapper;

    @Autowired
    private SysAuthTokenMapper authTokenMapper;

    @Autowired
    private SysSmsTemplateMapper smsTemplateMapper;

    @Autowired
    private SysTenantService tenantService;

    @Override
    @Transactional
    public Map<String, Object> getAuthCode(Integer tenantId,
                                           String  phone,
                                           String  bizType) {

        Map<String, Object> ret = new HashMap<String, Object>();


        Integer mTenantId = tenantService.getConfigTenantId(tenantId);

        Integer smsChannelType = 1; // 1 -- alidayu  2 -- baifentonglian

        //1. signature
        QueryWrapper<SysTenantDictConf> queryMapper = new QueryWrapper<SysTenantDictConf>();
        queryMapper.lambda()
                .eq(SysTenantDictConf::getTenantId, mTenantId)
                .eq(SysTenantDictConf::getDictId, "smsSignature");
        SysTenantDictConf tenantDictConf = tenantDictConfMapper.selectOne(queryMapper);

        String smsSignature = tenantDictConf != null?tenantDictConf.getDictValue(): null;

        if (StringUtils.isEmpty(smsSignature)) {
            smsSignature = "右脑CRM";
        }

        //2. template configuration
        String msgTpl = "{{code}}（验证码，十分钟内有效）";//{0}  右脑CRM

        QueryWrapper<SysSmsTemplate> smstmpltQR= new QueryWrapper<>();
        smstmpltQR.lambda().eq(SysSmsTemplate::getTenantId,mTenantId)
                .eq(SysSmsTemplate::getTplkey,bizType);
        SysSmsTemplate smsTemplate = smsTemplateMapper.selectOne(smstmpltQR);
        if (smsTemplate == null
                ||!smsTemplate.getEnabled()
                ||!"2".equals(smsTemplate.getType())
                ||StringUtils.isEmpty(smsTemplate.getContent())
                )
        {

        }
        else
        {
            msgTpl = smsTemplate.getContent();
            msgTpl = msgTpl + " 【"+smsSignature+"】";
        }



        //3. generate AuthCode
        String AuthCode  = SimpleStringUtil.getRandomString(4);


        HashMap<String,String> placeholderMap = new HashMap<>();
        placeholderMap.put("code",AuthCode);


        Date now = new Date();

        try {
            SysSms sms = sendSms(tenantId,
                    mTenantId,
                    phone,
                    "",
                    msgTpl,

                    placeholderMap,
                    now);

            //6. write sms and auth code to db
            if(sms.isSendSuccess()){
    //            if("test".equals(environment))
    //            {
    //                //测试环境,直接返回验证码
    //                ret.put("authCode", AuthCode);
    //            }

                String AuthToken = UUID.randomUUID().toString();
                ret.put("authToken", AuthToken);

                SysAuthToken token = new SysAuthToken();

                token.setTenantId(tenantId);
                token.setSendId(phone);
                token.setSendIdType("phone");
                token.setAuthToken(AuthToken);
                token.setAuthCode(AuthCode);
                token.setAuthBizType(bizType);
                token.setApplyTime(now);
                token.setValidTime(SimpleDateUtil.addMinsForDate(now, 120));
                authTokenMapper.insert(token);

                return MsgUtil.retMap(
                        RetObjConstant.AUTH_CODE,
                        ret
                );
            }else{
                return  MsgUtil.retMsgMap(MsgConstant.FAILED_TO_SENDSMS);
            }
        }catch(SmsException smse){
            return  MsgUtil.retMsgMap(MsgConstant.FAILED_RET,smse.getMessage());
        }

    }

    /**
     *
     * @Author：ruixj
     * @CreateTime：上午9:57:19
     * @Description：校验验证码
     * @param tenantId
     * @param phone
     * @param bizType
     * @param sendType
     * @param validityCode
     * @return
     */
    public boolean isValidAuthCode(Integer tenantId,
                                   String phone,
                                   String bizType,
                                   String sendType,
                                   String validityCode){
        String smsPropFileName       = "config.properties";
        String entironment           = PropertiesFileUtil.getPro(smsPropFileName, "entironment");
        String productPhoneWhiteList = PropertiesFileUtil.getPro(smsPropFileName, "productPhoneWhiteList");
        if (("test".equals(entironment)
                || ("product".equals(entironment) && productPhoneWhiteList.indexOf(phone) != -1))
        )
        {//测试环境,生产环境白名单中的手机不校验验证码
            return true;
        }
        Date now = new Date();
        QueryWrapper<SysAuthToken> validTokenQR = new QueryWrapper<SysAuthToken>();
        validTokenQR.lambda().eq(SysAuthToken::getSendId,phone)
                .eq(SysAuthToken::getSendIdType,sendType)
                .eq(SysAuthToken::getAuthBizType,bizType)
                .eq(SysAuthToken::getAuthCode,validityCode)
                .gt(SysAuthToken::getValidTime,now);
        List<SysAuthToken> authTokens = authTokenMapper.selectList(validTokenQR);
        if (authTokens.size() >= 1) {
            SysAuthToken authToken = authTokens.get(0);
            authToken.setVerifyTime(now);
            authToken.setVerifyResult("success");
            authTokenMapper.updateById(authToken);
            return true;
        } else {
            return false;
        }
    }

    /**
     *
     * @Author：ruixj
     * @CreateTime：上午9:57:24
     * @Description：校验验证码
     * @param authToken   短信令牌
     * @param authCode  短信验证
     * @return retcode为ok表示成功  phone为本次校验成功的手机号
     */
    public Map isValidAuthCode(String authToken, String authCode){
        Map<String, Object> ret =  new HashMap<String, Object>() ;

        if(!StringUtils.isEmpty(authToken)
                && !StringUtils.isEmpty(authCode))
        {
            Date now = new Date();
            QueryWrapper<SysAuthToken> validTokenQR = new QueryWrapper<SysAuthToken>();
            validTokenQR.lambda().eq(SysAuthToken::getAuthToken,authToken);
            SysAuthToken auth = authTokenMapper.selectOne(validTokenQR);

//            if(auth!=null && auth.getValidTime().before(now)){
//                ret =  MsgUtil.retMsgMap(MsgConstant.FAILED_RET,"验证码超时，请重新获取验证码");
//                return ret;
//
//            }

            if(auth!=null && !authCode.equals(auth.getAuthCode())){
                ret =  MsgUtil.retMsgMap(MsgConstant.FAILED_RET,"验证码错误");
                return ret;

            }

            //有效期内，验证码正确
            //更新验证时间
            auth.setValidTime(now);
            auth.setVerifyResult("success");
            authTokenMapper.updateById(auth);

            ret.put("phone", auth.getSendId());
            ret = MsgUtil.retMap("authresult",ret);

            return ret;

        }
        return ret;
    }

    @Override
    public Map sendNotifySms(Integer tenantId,
                             String bizType,
                             String phone,
                             Map<String,String> placeholderMap)
    {
        Integer mTenantId = tenantService.getConfigTenantId(tenantId);
        Map<String, Object> ret = null;

        //1. signature
        QueryWrapper<SysTenantDictConf> queryMapper = new QueryWrapper<SysTenantDictConf>();
        queryMapper.lambda()
                .eq(SysTenantDictConf::getTenantId, mTenantId)
                .eq(SysTenantDictConf::getDictId, "smsSignature");
        SysTenantDictConf tenantDictConf = tenantDictConfMapper.selectOne(queryMapper);
        String smsSignature = tenantDictConf.getDictValue();

        if(StringUtils.isEmpty(smsSignature))
        {
            ret =  MsgUtil.retMsgMap(MsgConstant.FAILED_RET,"未配置短信签名");
            return ret;
        }

        //2. sms template
        QueryWrapper<SysSmsTemplate> smstmpltQR= new QueryWrapper<>();
        smstmpltQR.lambda().eq(SysSmsTemplate::getTenantId,tenantId)
                .eq(SysSmsTemplate::getTplkey,bizType);
        SysSmsTemplate smsTemplate = smsTemplateMapper.selectOne(smstmpltQR);
        if(smsTemplate==null)
        {

            ret =  MsgUtil.retMsgMap(MsgConstant.FAILED_RET,"未配置短信模板");
            return ret;
        }
        if(!"1".equals(smsTemplate.getType())||StringUtils.isEmpty(smsTemplate.getContent()))
        {

            ret =  MsgUtil.retMsgMap(MsgConstant.FAILED_RET,"短信模板配置错误。");
            return ret;
        }

        if(!smsTemplate.getEnabled()){

            ret =  MsgUtil.retMsgMap(MsgConstant.FAILED_RET,"短信模板未启用。");
            return ret;
        }

        String megTpl= smsTemplate.getContent()+" 【"+smsSignature+"】";

        //3. send sms
        Date now = new Date();
        try {
            SysSms sms = sendSms(tenantId,
                    mTenantId,
                    phone,
                    "",
                    megTpl,
                    placeholderMap,
                    now);

            if (sms != null && sms.isSendSuccess()) {
                ret =  MsgUtil.retMsgMap(MsgConstant.SUCCESS_RET,"发送成功。");
            } else {
                ret =  MsgUtil.retMsgMap(MsgConstant.FAILED_RET,"发送失败。");
            }
        }catch(SmsException smse){
            ret =  MsgUtil.retMsgMap(MsgConstant.FAILED_RET,smse.getMessage());
        }
        return ret;
    }

    private SysSms sendSms(Integer            tenantId,
                           Integer            mTenantId,
                           String             phone,
                           String             smsType,
                           String msgTpl,

                           Map<String,String> placeholderMap,
                           Date               now
                           ) throws SmsException
    {
        SysTenantDictConf tenantDictConf = null;
        Integer smsChannelType = 1; // 1 -- alidayu  2 -- baifentonglian
        String sequenceId = SimpleDateUtil.toString(new Date(), "yyyyMMddhhmmssSSS");
        SysSms sms = null ;


        String smsPropFileName       = "config.properties";
        String environment           = PropertiesFileUtil.getPro(smsPropFileName, "environment");
        String allowTestPhone        = PropertiesFileUtil.getPro(smsPropFileName, "allowTestPhone");
        String productPhoneWhiteList = PropertiesFileUtil.getPro(smsPropFileName, "productPhoneWhiteList");

        //5. send sms by channel
        if(smsChannelType == 2) {
            if(placeholderMap != null)
            {
                Iterator<Map.Entry<String, String>> iter = placeholderMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry<String, String> entry = iter.next();
                    String mk = entry.getKey();
                    String mv = entry.getValue();
                    if(msgTpl.indexOf("{{"+mk+"}}")!=-1)
                    {
                        msgTpl = msgTpl.replaceAll("\\{\\{"+mk+"\\}\\}", mv);
                    }
                }
            }

            //5.2 account configuration
            String smsAccDictValue = null;
            String[] smsAccArr = null;


            QueryWrapper<SysTenantDictConf> qrAcc = new QueryWrapper<SysTenantDictConf>();
            if (StringUtils.isEmpty(smsType)) {

                qrAcc.lambda()
                        .eq(SysTenantDictConf::getTenantId, mTenantId)
                        .eq(SysTenantDictConf::getDictId, "smsAcc");
                tenantDictConf = tenantDictConfMapper.selectOne(qrAcc);
                smsAccDictValue = tenantDictConf.getDictValue();
            } else {
                qrAcc.lambda()
                        .eq(SysTenantDictConf::getTenantId, mTenantId)
                        .eq(SysTenantDictConf::getDictId, "smsAcc." + smsType);
                tenantDictConf = tenantDictConfMapper.selectOne(qrAcc);
                smsAccDictValue = tenantDictConf.getDictValue();

            }
            if (StringUtils.isEmpty(smsAccDictValue)) {
                log.error("短信通道账号未配置");
                throw new SmsException("短信通道账号未配置");
            }

            //5.3 send sms
            String url       = "bftlUrl";
            String smsReqUrl = PropertiesFileUtil.getPro(smsPropFileName, url);

            try {
                smsReqUrl = MessageFormat.format(smsReqUrl,
                        smsAccArr[0],
                        smsAccArr[1],
                        smsAccArr[2],
                        phone,
                        URLEncoder.encode(msgTpl, "UTF-8")
                );
            } catch (UnsupportedEncodingException e) {
                log.error("短信内容编码发生异常", e);
                throw new SmsException("短信内容编码发生异常");
            }


            String responseData          = null;

            if (("product".equals(environment) && productPhoneWhiteList.indexOf(phone) == -1)
                    || ("test".equals(environment) && allowTestPhone.indexOf(phone) != -1))
            {
                //当前环境为product，或配置在白名单中的手机，会真实下发短信，否则不会真实下发
                try {
                    responseData = HttpClientUtils.get(smsReqUrl, "UTF-8", 3000, 3000);
                } catch (ConnectTimeoutException e) {
                    log.error("请求百分通联发生异常", e);
                    throw new SmsException("请求百分通联发生异常");

                } catch (SocketTimeoutException e) {
                    log.error("请求百分通联发生异常", e);
                    throw new SmsException("请求百分通联发生异常");
                } catch (Exception e) {
                    log.error("请求百分通联发生异常", e);
                    throw new SmsException("请求百分通联发生异常");
                }
            }
            else {//返回模拟成功
                log.info("没有真正下发短信，返回模拟成功结果，如果需要真实下发，请将手机号配置到sms.properties的allowTestPhone中，多手机号以逗号分隔");
                responseData = "<?xml version=\"1.0\" encoding=\"utf-8\"?><CSubmitState xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://tempuri.org/\"><State>0</State><MsgID>" + sequenceId + "</MsgID><MsgState>提交成功</MsgState><Reserve>0</Reserve></CSubmitState>";
            }

            //5.3 process send feedback
            if (responseData != null && !"".equals(responseData) && responseData.length() > 0) {
                responseData = responseData == null ? "" : responseData.trim();
                String result = null;
                String msgId = null;
                String submitStat = "fail";
                String msgStat = "";
                try {
                    log.info("百分通联通道-收到返回:" + responseData);
                    Element root = XmlUtil.getRootElement(responseData);

                    result  = XmlUtil.getValueAt(root, "State");
                    msgId   = XmlUtil.getValueAt(root, "MsgID");
                    msgStat = XmlUtil.getValueAt(root, "MsgState");
                    if ("0".equals(result))
                        submitStat = "suc";
                    else
                        submitStat = "fail";

                } catch (Exception e) {
                }
                sms = new SysSms();
                sms.setTenantId(tenantId);
                sms.setSmsChannel("bftl");
                sms.setPhone(phone);
                sms.setCreate_time(now);
                sms.setUpdate_time(now);
                sms.setMsgContent(msgTpl);
                sms.setMsgId(msgId);
                sms.setSubmitStat(submitStat);
                sms.setDeliverStat(msgStat);
                sms.setSrcId("");

            } else {
                log.error("百分通联通道-返回异常数据");
                throw new SmsException("百分通联通道-返回异常数据");
            }
        }
        else {
            //替换大鱼短信中的模板的变量，计算6位随机码，这里有个坑，"code"名称和大于的模板管理中
            // "你的注册验证码是${code}，五分钟有效，请勿告诉其他人"中的变量
            //名称要一致
            String jsonContent = null;
            try {
                jsonContent  = JsonUtil.toJson(placeholderMap);
            }
            catch(JsonProcessingException jpe){
                throw new SmsException("阿里大鱼发送失败！");
            }
            Map<String, String> paramMap = new HashMap<String, String>();
            paramMap.put("phoneNumber", phone);
            paramMap.put("msgSign", SmsConstant.msgSign);
            paramMap.put("templateCode", SmsConstant.templateCode);
            paramMap.put("jsonContent", jsonContent);
            //paramMap.put("code", AuthCode);
            SendSmsResponse sendSmsResponse = null;
            String submitStat = "fail";
            try{
                sendSmsResponse = AliyunMessageUtil.sendSms(paramMap);
                if(sendSmsResponse.getCode() != null
                        && sendSmsResponse.getCode().equals("OK")) {
                    submitStat = "suc";
                }
                else
                {
                    log.error("阿里大鱼返回码:" + sendSmsResponse.getCode());
                    //throw new SmsException("阿里大鱼发送失败！");
                }
            }catch(Exception e){
                log.error(e.getMessage());
                throw new SmsException("阿里大鱼发送失败！");
            }

            sms = new SysSms();
            sms.setTenantId(tenantId);
            sms.setSmsChannel("alidayu");
            sms.setPhone(phone);
            sms.setCreate_time(now);
            sms.setUpdate_time(now);
            sms.setMsgContent(SmsConstant.templateCode);
            sms.setMsgId(sendSmsResponse.getBizId());
            sms.setSubmitStat(submitStat);
            sms.setDeliverStat(submitStat);

            sms.setSrcId("");
        }

        smsMapper.insert(sms);

        return sms;
    }

}

